package com.tuozixuan.config;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {

	/** 缓存key前缀 */
	private static final String keyPrefix = "CACHE:";

	@Autowired
	private CachingProperties cachingProperties;

	@Bean
	@Override
	public KeyGenerator keyGenerator() {
		return (target, method, params) -> {
			// key格式：CACHE:简单类名:方法名:参数1_参数2
			StringJoiner joiner = new StringJoiner(":", keyPrefix, "");
			joiner.add(target.getClass().getSimpleName());
			joiner.add(method.getName());

			String paramStr = Stream.of(params).map(Object::toString).collect(Collectors.joining("_"));
			joiner.add(paramStr);
			return joiner.toString();
		};
	}

	/**
	 * 管理缓存
	 */
	@Bean
	public CacheManager cacheManager(LettuceConnectionFactory factory) {
		// 缓存配置对象
		RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
				// 设置缓存的默认超时时间
				.entryTtl(Duration.ofSeconds(cachingProperties.getDefaultExpiration()))
				// 如果是空值，不缓存
				.disableCachingNullValues()
				// 设置key序列化器
				.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
				// 设置value序列化器
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((valueSerializer())));

		Map<String, RedisCacheConfiguration> initRedisCacheConfiguration = new HashMap<>();
		Map<String, Long> expireConfigMap = cachingProperties.getExpires();
		if (Objects.nonNull(expireConfigMap)) {
			for (Map.Entry<String, Long> expireConfigEntry : expireConfigMap.entrySet()) {
				initRedisCacheConfiguration.put(expireConfigEntry.getKey(),
						this.buildRedisCacheConfigurationByTtl(Duration.ofSeconds(expireConfigEntry.getValue())));
			}
		}

		return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory))
				.cacheDefaults(cacheConfig).withInitialCacheConfigurations(initRedisCacheConfiguration).build();
	}

	/**
	 * 根据ttl构建缓存配置
	 * 
	 * @param ttl Time to Live
	 * @return RedisCacheConfiguration 缓存配置
	 */
	private RedisCacheConfiguration buildRedisCacheConfigurationByTtl(Duration ttl) {
		// 缓存配置对象
		RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
				// 设置缓存的超时时间
				.entryTtl(ttl)
				// 如果是空值，不缓存
				.disableCachingNullValues()
				// 设置key序列化器
				.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
				// 设置value序列化器
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
		return cacheConfig;
	}

	/**
	 * key序列化
	 */
	private RedisSerializer<String> keySerializer() {
		return new StringRedisSerializer();
	}

	/**
	 * value序列化
	 */
	private RedisSerializer<Object> valueSerializer() {
		return new GenericJackson2JsonRedisSerializer();
	}

}
